#pragma once

#include <ddblk/apds/combine_int.h>
#include <dbg.h>


typedef struct _cacdsmsynnode
{
	APDSFUTURE_DECL
	union{
		struct{
			hword_t	cid;
			apdspeff_t eff;
		};
		word_t cid_eff;
	};
	struct _cacdsmsynnode *next;
} _cacdsmsynnode_t;

typedef struct _cacdsmsyn
{
	_cacdsmsynnode_t *tail;
} _cacdsmsyn_t;

DECL_COMBINE(cacdsmsyn)

SAI void __cacdsmsyn_init(_cacdsmsyn_t *s)
{
	s->tail = NULL;
}
SAI void *__cacdsmsyn_destruct(_cacdsmsyn_t *s)
{
	ASSERT(s->tail == NULL);
	return (void *)s;
}

SAI _cacdsmsynnode_t *__cacdsmsyn_fetch(_cacdsmsyn_t *s, apdsfunc_t *pfunc, apdsarg_t *args, _cacdsmsynnode_t *node, apdstinfo_t *tinfo){
	PARAM_USED(tinfo, s);
/* 	cid_t cur_cid = tinfo->cid;
	word_t cid_eff = new_word(APDS_GET_COREEFF(cur_cid), cur_cid); */

	apds_movargs(&node->args, args);
	node->func = pfunc;

/* 	node->cid_eff = cid_eff; */
	node->flag = COMBINE_FLG_WAIT;
	node->next = NULL;
	/* cmb(); */
	return node;
}
SAI apdsret_t __cacdsmsyn_wait(_cacdsmsyn_t *s, _cacdsmsynnode_t *node, apdstinfo_t *tinfo, INFUN(cmbn_wait, waitpolicy)){
	waitcnt_t cnt = -1;
	_cacdsmsynnode_t *cur_node, *next_node;
	lkword_t flag_wait = COMBINE_FLG_WAIT;
	lkword_t flag;
	_cacdsmsynnode_t *my_node, *my_pred_node;

	cid_t cur_cid = tinfo->cid;
	/* hword_t help_quota = APDS_GET_COREQUOTA(cur_cid) - 1; */
	hword_t help_quota = APDS_HELP_QUOTA - 1;
	/* ASSERT(help_quota > 0); */
	apdspeff_t cur_eff = APDS_GET_COREEFF(cur_cid);
	word_t cid_eff = new_word(APDS_GET_COREEFF(cur_cid), cur_cid);

	my_node = node;
	my_pred_node = swp_rel(&s->tail, my_node);
	if (my_pred_node)
	{	
		atm_st(&my_pred_node->cid_eff, cid_eff);
		atm_strel(&my_pred_node->next, my_node);
		flag = atm_ld(&my_node->flag);
		while (flag == COMBINE_FLG_WAIT)
		{
			cnt = waitpolicy->waitf(&my_node->flag, &flag_wait, cnt);
			flag = atm_ld(&my_node->flag);
		}
		if (flag == COMBINE_FLG_COMPLETED)
		{
			mp_rmb(); /* 同一cache行且存在读依赖 */
			return apds_args_ret(&my_node->args);
		}
	}
	mp_rmb();


	/* 为什么要限制0 -> 1 -> 2, 是因为有memory reclaim的问题 */
	cur_node = my_node; 

	apds_set_ret(&cur_node->args, apds_sync_single(cur_node->func, &cur_node->args));
	/* atm_st(&cur_node->flag, COMBINE_FLG_COMPLETED); */
	
	next_node = atm_ld(&cur_node->next);
	/* APDS_PREFETCH(next_node); */

	if ((help_quota > 0) && next_node && next_node->next){
		cur_node = next_node;
		do
		{
			/* 帮助 */
			/* cmb(); */
			apds_set_ret(&cur_node->args, apds_sync_single(cur_node->func, &cur_node->args));
			/* cmb(); */
			word_t cid_eff = atm_ld(&cur_node->cid_eff);
			next_node = atm_ld(&cur_node->next);
			/* APDS_PREFETCH(next_node); */

			/* 释放, 确保读写操作都不会被重排到后方 */
			atm_strel(&cur_node->flag, COMBINE_FLG_COMPLETED);

			/* 判断是否需要放弃 */
			help_quota = cac_next_helpquota(COMBINE_TEFF(cid_eff), COMBINE_TCID(cid_eff), &cur_eff, cur_cid, help_quota);
			/* quota补偿防止额度消耗完时传递给一个低效率的线程，造成大->小->大的浪费 */
			if ((help_quota == 0) && (cur_eff > COMBINE_TEFF(cid_eff))) { cur_eff -= 1; help_quota += 1; };
			
			if ((help_quota == 0) || !next_node || !next_node->next) break;
			cur_node = next_node;
			/* 下个节点不为空 */
		} while (TRUE);
	}
	/* apdsret_t ret = apds_args_ret(&my_node->args); */
	if (!next_node)
	{
		_cacdsmsynnode_t *oldval = cur_node;
		if (cas_rel_s(&s->tail, &oldval, NULL))
		{
			return apds_args_ret(&my_node->args);
		}
		next_node = atm_ld(&cur_node->next);
		while (!next_node)
		{
			PAUSE();
			next_node = atm_ld(&cur_node->next);
		}
	}
	// 处理尾部
	atm_strel(&next_node->flag, COMBINE_FLG_OWNED);

	
	lkword_t flag_owned = COMBINE_FLG_OWNED;
	waitpolicy->wakef(&next_node->flag, &flag_owned);
	return apds_args_ret(&my_node->args);
}

PARAM_NONULL(1) SAI _cacdsmsyn_t *_cacdsmsyn_init(void *ptr, size_t size, INFUN(cmbn_node, nodemgr))
{
	PARAM_USED(nodemgr);
	if (size < sizeof(_cacdsmsyn_t)) return NULL;
	_cacdsmsyn_t *s = (_cacdsmsyn_t *)ptr;
	__cacdsmsyn_init(s);
	return s;
}
PARAM_NONULL(1) SAI void *_cacdsmsyn_destruct(_cacdsmsyn_t *s, INFUN(cmbn_node, nodemgr)){
	PARAM_USED(nodemgr);
	void *rptr = __cacdsmsyn_destruct(s);
	return rptr;
}
PARAM_NONULL(1, 2, 3) SAI _cacdsmsynnode_t *_cacdsmsyn_fetch(_cacdsmsyn_t *s, apdsfunc_t *pfunc, apdsarg_t *args, apdstinfo_t *tinfo, INFUN(cmbn_node, nodemgr)){
	_cacdsmsynnode_t *node = (_cacdsmsynnode_t *)nodemgr->get_lknode(sizeof(_cacdsmsynnode_t));
	return __cacdsmsyn_fetch(s, pfunc, args, node, tinfo);
}
PARAM_NONULL(1, 2) SAI apdsret_t _cacdsmsyn_wait(_cacdsmsyn_t *s, _cacdsmsynnode_t *node, apdstinfo_t *tinfo, INFUN(cmbn_wait, waitpolicy), INFUN(cmbn_node, nodemgr)){
	apdsret_t ret = __cacdsmsyn_wait(s, node, tinfo, waitpolicy);
	nodemgr->ret_lknode(node, sizeof(_cacdsmsynnode_t));
	return ret;
}
